﻿using System;
using System.Collections.Generic;
using System.Linq;
using OS.Infrastructure.Domain;
using OS.Infrastructure.Domain.UnitOfWork;
using TaskKernel.Configuration;
using TaskKernel.DTO;
using TaskKernel.Domain;
using TaskKernel.Domain.Interface;
using TaskKernel.Domain.Services.Action;
using TaskKernel.Domain.Services.Mail;
using TaskKernel.Domain.Services.Permission;
using TaskKernel.Domain.Services.Role;

namespace TaskWebApplicationUI.PresenterLayer
{
    public class EditTaskPresenter : AbstractPresenter<IEditTaskView>
    {
        private readonly ICommandRepository<Task> _command;
        private readonly IMessageService _messageService;
        private readonly IQueryAccount _queryAccount;
        private readonly IQueryDepartment _queryDepartment;
        private readonly IQueryTask _queryTask;
        private readonly ITaskAction _taskAction;
        private readonly IUnitOfWorkFactory _uow;
        private Account _currentAccount;
        private Department _currentDepartment;

        public EditTaskPresenter(IQueryDepartment queryDepartment, IQueryAccount queryAccount, IUnitOfWorkFactory uow,
                                 ITaskAction taskAction, IQueryTask queryTask, IMessageService messageService,
                                 ICommandRepository<Task> command)
        {
            _queryDepartment = queryDepartment;
            _queryAccount = queryAccount;
            _uow = uow;
            _taskAction = taskAction;
            _queryTask = queryTask;
            _messageService = messageService;
            _command = command;
        }

        #region EventParticipants

        public Account CurrentAccount
        {
            get
            {
                if (_currentAccount != null)
                    return _currentAccount;
                if (!String.IsNullOrEmpty(View.LogonUser))
                {
                    _currentAccount = _queryAccount.Get(View.LogonUser);
                    if (_currentAccount != null)
                        return _currentAccount;
                }
                throw new NotImplementedException("Don't know who are you...");
            }
        }

        public Department CurrentDepartment
        {
            get
            {
                if (_currentDepartment != null)
                    return _currentDepartment;
                if (!String.IsNullOrEmpty(View.WebPath))
                {
                    _currentDepartment = _queryDepartment.GetDepartmentByWebPath(View.WebPath);
                    if (_currentDepartment != null) return _currentDepartment;
                    if (_queryDepartment == null)
                        throw new NotImplementedException("queryDepartment is null");
                }
                throw new NotImplementedException("Don't know where are you...");
            }
        }

        public void OnViewInitialized()
        {
            using (var z = _uow.Create())
            {
                View.DepartmentName = CurrentDepartment.DepartmentName;
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                if (task == null) return;
                if (task.Department != CurrentDepartment) View.RedirectWebPath = task.Department.WebPath;
                else
                {
                    var taskAction = GetAction(task);
                    if (taskAction.ViewTask(task, CurrentAccount))
                    {
                        _command.Save(task);
                        if (z.Commit())
                        {
                            LoadPagePermissions(task);
                            LoadDateCreationTask(task);
                            LoadHistoryEntries(task);
                            LoadTaskName(task);
                            LoadExpirationDate(task);
                            LoadTaskOwner(task);
                            LoadTaskNumber(task);
                            LoadExecutors(task);
                            LoadStatusTask(task);
                            LoadPriority(task);
                            LoadPercentComplete(task);
                            LoadParticipants(task);
                            LoadReminder(task);
                            LoadDepartments(task);
                            LoadFiles(task, taskAction);
                            LoadTag(task);
                            LoadRatings(task);
                            LoadExecutionProcess(task);
                        }
                    }
                }
                LoadAccounts();
            }
        }

        private void LoadExecutionProcess(Task task)
        {
            if (task.HasPartialWorkingPeriodFor(CurrentAccount)) View.TaskExecutionInProgress = true;
            View.ResultWorkingTime = task.GetResultWorkingTime(CurrentAccount);
        }

        private void LoadAccounts()
        {
            var accounts = _queryAccount.GetAllNotFiredAccounts();
            View.Users = accounts.ToViewWithEmptyValue();
        }

        public virtual void OnDelete()
        {
            //TODO: replace this logic to set flag "delete" for task entity in db
            //using (var z = _uow.Create())
            //{
            //    var idTask = Int32.Parse(View.TaskID);
            //    var task = _queryTask.GetTask(idTask);
            //    _command.Delete(task);
            //    z.Commit();
            //    ComplexRefresh(task);
            //}
        }

        private ITaskAction GetAction(Task task)
        {
            return new TaskServiceByRoleInTask(_taskAction, task, CurrentAccount).GetAction();
        }

        public virtual void OnSave()
        {
            using (var z = _uow.Create())
            {
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                var taskAction = GetAction(task);
                ComplexSave(task, taskAction);
                _command.Save(task);
                if (z.Commit())
                {
                    _messageService.ChangeNotification(task, CurrentAccount, "Задача изменена");
                    if (task.Department != CurrentDepartment)
                        View.RedirectWebPath = task.Department.WebPath;
                }
                ComplexRefresh(task);
            }
        }

        public virtual void OnSetRating()
        {
            using (var z = _uow.Create())
            {
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                var serviceRole = new TaskServiceByRoleInTask(_taskAction, task, CurrentAccount);
                var actionSerice = serviceRole.GetAction();
                actionSerice.SetRating(task, CurrentAccount, View.RatingValue, View.RatingText, serviceRole.Role.ToDescriptionString());
                _command.Save(task);
                z.Commit();
            }
        }

        public virtual void OnForceClose()
        {
            using (var z = _uow.Create())
            {
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                var taskAction = GetAction(task);
                ComplexSave(task, taskAction);
                taskAction.ForceCloseTask(task, CurrentAccount);
                _command.Save(task);
                if (z.Commit())
                    _messageService.ChangeNotification(task, CurrentAccount, "Задача закрыта");
                ComplexRefresh(task);
            }
        }

        private bool CheckRating(Task task, Account account)
        {
            // если нажавший на кнопку закрыть задачу является владельцем этой задачи ииеет в разделе роль админа или исполнителя, то оценка не нужна
            var roleInDepartment = new RoleInDepartment(task.Department, account);
            var role = roleInDepartment.GetRole();
            if (task.Owner.UID == CurrentAccount.UID && role != RoleEnum.Other) return true;
            //если у задачи нет оценки от владельца и департамент со статусом обязательной оценки от владельца 
            if (!task.ExistRatingFromOwner() && CurrentDepartment.RatingRequired)
            {
                View.ErrorMessage = TaskKernelResources.RatingRequired;
                return false;
            }
            return true;
        }

        public virtual void OnClose()
        {
            using (var z = _uow.Create())
            {
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                var taskAction = GetAction(task);
                ComplexSave(task, taskAction);
                var successClose = false;
                if (CheckRating(task, CurrentAccount))
                {
                    if (taskAction.CloseTask(task, CurrentAccount))
                    {
                        _command.Save(task);
                        if (z.Commit())
                        {
                            _messageService.ChangeNotification(task, CurrentAccount, "Задача закрыта");
                            successClose = true; //раз закрыли задачу, то не пытаемся подать запрос на закрытие
                        }
                    }
                }
                if (!successClose && taskAction.RequestCloseTask(task, CurrentAccount))
                {
                    _command.Save(task);
                    if (z.Commit()) {
                        _messageService.ChangeNotification(task, CurrentAccount, "Подан запрос на закрытие задачи");
                        View.ErrorMessage = ""; //перед попыткой закрытия мы проверили нужна ли оценка владельца, но т.к. мы не смогли закыть задачУ, то для запроса на закрытие это не нужно.
                    }
                }
                ComplexRefresh(task);
            }
        }

        public virtual void OnSentContent()
        {
            using (var z = _uow.Create())
            {
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                _messageService.ContentAlexandrovaNotification(task, CurrentAccount);
            }
        }

        public void OnUnClose()
        {
            using (var z = _uow.Create())
            {
                var idTask = View.TaskID;
                var task = _queryTask.GetTask(idTask);
                var taskAction = GetAction(task);
                taskAction.UnCloseTask(task, CurrentAccount);
                _command.Save(task);
                z.Commit();
                ComplexRefresh(task);
            }
        }

        public virtual void OnDelParticipant()
        {
            var models = View.Participants;
            models.Remove(View.DeletedParticipant);
            View.Participants = models;
            ReplaceParticipant(View.DeletedParticipant, false);
        }

        private void ReplaceParticipant(UniversalModelDDL universalModel, bool status)
        {
            var changedParticipant = new ParticipantsModelDDL(universalModel, status);
            var changedParticipants = View.ChangedParticipants;
            changedParticipants.Remove(changedParticipant);
            changedParticipants.Add(changedParticipant);
            View.ChangedParticipants = changedParticipants;
        }

        public virtual void OnAddParticipant()
        {
            using (var z = _uow.Create())
            {
                var id = View.SelectedUser;
                if (id != Guid.Empty)
                {
                    var participant = _queryAccount.Get(id);
                    if (participant != null)
                    {
                        var models = View.Participants;
                        var universalModel = new UniversalModelDDL(participant.UID, participant.FullName);
                        if (!models.Contains(universalModel))
                        {
                            models.Add(universalModel);
                            View.Participants = models;
                            ReplaceParticipant(universalModel, true);
                        }
                    }
                }
            }
        }

        public virtual void OnSearchParticipant()
        {
            using (var z = _uow.Create())
            {
                var word = View.UsersSearchString;
                if (!String.IsNullOrEmpty(word))
                {
                    var accounts = _queryAccount.GetAccountListBySearchWord(word);
                    View.Users = accounts.ToView();
                }
                else
                    LoadAccounts();
            }
        }

        #endregion

        #region Load

        private void RefreshHistoryView(Guid id, bool systemMessage)
        {
            var task = _queryTask.GetTask(id);
            if (null != task)
            {
                IList<HistoryModel> fileModel;
                View.HistoryEntries = task.HistoryEntryToView(systemMessage, out fileModel);
                if (fileModel != null)
                    View.FileHistoryEntries = fileModel;
            }
        }

        private void ComplexRefresh(Task task)
        {
            LoadStatusTask(task);
            LoadPercentComplete(task);
            RefreshHistoryView(task.UID, View.SystemMessage);
            View.HistoryEntryText = String.Empty;
        }

        private void LoadTag(Task task)
        {
            if (!String.IsNullOrEmpty(task.Tag))
                View.Tag = task.Tag;
        }

        private void LoadRatings(Task task)
        {
            var rating = task.GetRating(CurrentAccount);
            if (rating != null)
            {
                View.RatingText = rating.RatingText;
                View.RatingValue = rating.RatingValue;
            }
        }

        private void LoadFiles(Task task, ITaskAction taskAction)
        {
            View.Filez = taskAction.LoadFiles(task).ToList();
        }

        private void LoadDepartments(Task task)
        {
            var department = task.Department;
            var departments = _queryDepartment.GetAllDepartments().ToList();
            if (!departments.Contains(department))
                departments.Add(department);
            View.Departments = departments.ToView();
            View.SelectedDepartment = department.UID;
        }

        private void LoadReminder(Task task)
        {
            var reminder = task.GetReminderFor(CurrentAccount);
            if (reminder != null)
            {
                View.ReminderEnable = true;
                View.ReminderDate = reminder.DateTime;
                View.ReminderNote = reminder.Note;
            }
        }

        private void LoadPagePermissions(Task task)
        {
            var role = GetRole(task, CurrentAccount);
            var i = new IPermission<TaskPermissions>[]{
                                                          new AdminClosedEditF(),
                                                          new ExecutorOwnerClosedEditF(),
                                                          new AdminUnclosedEditF(),
                                                          new ExecutorUnclosedEditF(),
                                                          new OwnerUnclosedClaimEditF(),
                                                          new OwnerUnclosedNotClaimEditF(),
                                                          new ParticipantUnclosedEditF()
                                                      };
            var t = new TaskPermissions(role, task);
            foreach (var permission in i)
                t.AddPermissions(permission);
            View.RolePermissions = t.GetPermissions();
        }

        /// <summary>
        /// получить абсолютную информацию о роли текущего пользователя в задаче и разделе, где она находится
        /// </summary>
        /// <param name="task"></param>
        /// <param name="account"></param>
        /// <returns></returns>
        private RoleEnum GetRole(Task task, Account account)
        {
            var roleInDepartment = new RoleInDepartment(task.Department, account);
            var role = roleInDepartment.GetRole();
            var roleInTask = new RoleInTask(task, account, role);
            role = roleInTask.GetRole();
            return role;
        }

        private void LoadExpirationDate(Task task)
        {
            if (null != task.DateExpiration)
                View.ExpirationDate = task.DateExpiration;
        }

        private void LoadPercentComplete(Task task)
        {
            View.PercentComplete = task.PercentComplete;
        }

        private void LoadParticipants(Task task)
        {
            var participants = task.ParticipantsWithOwner.Select(x => x.Login);
            View.Participants = participants.ToView();
        }

        private void LoadPriority(Task task)
        {
            View.Priority = (int)task.Priority;
        }

        private void LoadStatusTask(Task task)
        {
            View.TaskStatus = task.Status.TranslateStatus();
        }

        private void LoadExecutors(Task task)
        {
            var accountsDepartment = CurrentDepartment.Executors;
            View.Executors = accountsDepartment.ToView();
            if (null == task.Executor || !CurrentDepartment.Executors.Contains(task.Executor))
                View.SelectedExecutor = Guid.Empty;
            else
            {
                View.SelectedExecutor = task.Executor.UID;
                View.ExecutorName = task.Executor.FullName;
            }
        }

        private void LoadTaskNumber(Task task)
        {
            View.TaskID = task.UID;
        }

        private void LoadTaskOwner(Task task)
        {
            if (null != task.Owner)
                View.TaskOwner = task.Owner.FullName;
        }

        private void LoadTaskName(Task task)
        {
            View.TaskName = task.TaskName;
        }

        private void LoadHistoryEntries(Task task)
        {
            IList<HistoryModel> fileModel;
            View.HistoryEntries = task.HistoryEntryToView(View.SystemMessage, out fileModel);
            if (fileModel != null)
                View.FileHistoryEntries = fileModel;
        }

        private void LoadDateCreationTask(Task task)
        {
            View.DateCreate = task.DateCreate.ToString();
        }

        #endregion

        #region HistoryBuilder

        #endregion

        #region Save

        private void SaveFiles(Task task, ITaskAction taskAction)
        {
            foreach (var uploadedFile in View.Filez)
                taskAction.AddFile(task, uploadedFile, CurrentAccount);
        }

        private void SavePercentComplete(Task task, ITaskAction taskAction)
        {
            if (View.PercentComplete >= 0 && View.PercentComplete <= 100)
                taskAction.SetPercentComplete(task, View.PercentComplete, CurrentAccount);
        }

        private void ComplexSave(Task task, ITaskAction taskAction)
        {
            SaveParticipants(task, taskAction);
            SaveExecutors(task, taskAction);
            SaveExpirationDate(task, taskAction);
            SaveHistoryEntries(task, taskAction);
            SavePriority(task, taskAction);
            SavePercentComplete(task, taskAction);
            SaveReminder(task, taskAction);
            SaveDepartment(task, taskAction);
            SaveFiles(task, taskAction);
            SaveTag(task, taskAction);
            SaveTaskName(task, taskAction);
        }

        private void SaveTaskName(Task task, ITaskAction taskAction)
        {
            if (!String.IsNullOrEmpty(View.TaskName))
                taskAction.ChangeTaskName(task, View.TaskName, CurrentAccount);
        }

        private void SaveTag(Task task, ITaskAction taskAction)
        {
            if (!String.IsNullOrEmpty(View.Tag))
                taskAction.SetTag(task, View.Tag, CurrentAccount);
        }

        private void SaveDepartment(Task task, ITaskAction taskAction)
        {
            var department = _queryDepartment.GetDepartment(View.SelectedDepartment);
            if (department != null)
                taskAction.SetDepartment(task, department, CurrentAccount);
        }

        private void SaveReminder(Task task, ITaskAction taskAction)
        {
            if (View.ReminderDate.HasValue)
                taskAction.AddReminder(task, View.ReminderDate.Value, View.ReminderNote, CurrentAccount);
            else
                taskAction.DeleteReminder(task, CurrentAccount);
        }

        private void SaveExpirationDate(Task task, ITaskAction taskAction)
        {
            taskAction.SetExpirationDate(task, View.ExpirationDate, CurrentAccount);
        }

        private void SavePriority(Task task, ITaskAction taskAction)
        {
            taskAction.SetPriority(task, (TaskPriority)View.Priority);
        }

        private void SaveHistoryEntries(Task task, ITaskAction taskAction)
        {
            var historyText = View.HistoryEntryText;
            if (!String.IsNullOrEmpty(historyText))
            {
                historyText = historyText.Replace("</form>", "");
                historyText = historyText.Replace("<form", "");
                taskAction.AddComment(task, historyText, CurrentAccount);
            }
        }

        private void SaveParticipants(Task task, ITaskAction taskAction)
        {
            if (null != View.ChangedParticipants)
            {
                if (View.ChangedParticipants.Any())
                {
                    if (View.ChangedParticipants.Where(x => x.IsAdded).Count() > 0)
                    {
                        taskAction.AddParticipant(task,
                                                  AccountsModelToDomainModel(
                                                      View.ChangedParticipants.Where(x => x.IsAdded)), CurrentAccount);
                    }
                    if (View.ChangedParticipants.Where(x => x.IsAdded == false).Count() > 0)
                    {
                        taskAction.DeleteParticipant(task,
                                                     AccountsModelToDomainModel(
                                                         View.ChangedParticipants.Where(x => x.IsAdded == false)),
                                                     CurrentAccount);
                    }
                }
            }
        }

        private void SaveExecutors(Task task, ITaskAction taskAction)
        {
            if (View.SelectedExecutor == Guid.Empty) taskAction.SetExecutor(task, null, CurrentAccount);
            else
            {
                var executor = _queryAccount.Get(View.SelectedExecutor);
                taskAction.SetExecutor(task, executor, CurrentAccount);
            }
        }

        #endregion

        #region ModelTransformation

        private IEnumerable<Account> AccountsModelToDomainModel(IEnumerable<ParticipantsModelDDL> models)
        {
            IList<Account> participants = new List<Account>();
            foreach (var view in models)
            {
                var account = _queryAccount.Get(view.ID);
                if (null != account)
                    participants.Add(account);
            }
            return participants;
        }

        #endregion

        public void OnRefreshHistory()
        {
            using (var z = _uow.Create())
                RefreshHistoryView(View.TaskID, View.SystemMessage);
        }

        public void OnStartTaskProcess()
        {
            using (var z = _uow.Create())
            {
                var task = _queryTask.GetTask(View.TaskID);
                var taskAction = GetAction(task);
                taskAction.StartWorkingPeriodTrigger(task, CurrentAccount);
                _command.Save(task);
                if (z.Commit())
                    View.TaskExecutionInProgress = true;
            }
        }

        public void OnPauseTaskProcess()
        {
            using (var z = _uow.Create())
            {
                var task = _queryTask.GetTask(View.TaskID);
                var taskAction = GetAction(task);
                var isStart = taskAction.StartWorkingPeriodTrigger(task, CurrentAccount);
                _command.Save(task);
                if (z.Commit())
                {
                    if (!isStart) View.ResultWorkingTime = task.GetResultWorkingTime(CurrentAccount);
                    View.TaskExecutionInProgress = false;
                }
            }
        }
    }
}